#pragma once

//#include <vector>
//#include "eightlayerim.h"
#include "searule.h"


using namespace std;
/**
 * This class represents a RuleSet that will be used as an individual in an Symbiotic EA. An object of this class 
 * has the following structure:
 * <RULE-SET> -> a set of <RULE>s
 * <RULE> -> a set of <CONDITION PARTS> + <RESULT>
 * <CONDITION PART> -> a set of [<NEIGHBOR> + <HISTORY STEP> + <CONDITION> + <NBVALUE>]s; e.g. N5 in H2 IS 1'
 * <NEIGHBOR> -> one of the neighbors of the neighborhood.
 * <HISTORY STEP> -> a number telling which time step (how far from now) this condition is referring to.
 * <CONDITION> -> [IS / ISNOT]
 * <RESULT> -> a Class Name 
 * <NBVALUE> -> a value from the possible values of the cell, e.g. 0,1,2
 */
class SEARuleSet //Symbiotic Evolutionary Algorithmm Rule Set.
{
private:
	/** A collection of rules, each telling if a given pixel with known neighborhood and known history is black or white 
	 *  in the next step.
	 */
	vector<SEARule> rule;
	int nBRadius;			//!< The radius of the neighborhood in which the current rulebase searches for the features.
	int maxHistorySteps;	//!< The maximum history steps of the pixels the ruleset will look into.

	/** Applies the ruleset on one single pixel with known neighborhoods in time and returns the value of that pixel in next timestep.*/
	int applyOnPixel (const PixelHistory&  pixel) const;
	SEARuleSet ();
public:
	/** Constructs an empty ruleset (no rule, zero size). */
	SEARuleSet (int nbRadius, int historySize);
	SEARuleSet (const SEARuleSet& other);
	SEARuleSet& operator= (const SEARuleSet& other);

	/** Returns the fitness of applying this ruleset on all the pixels of the given EightLyerIM object.
	 *  This function creates the layer lastLayer+1 based on applying the current ruleset to all pixels
	 *  with the history from firstLayer to lastLayer, and then compares it to the actual layer 'lastLayer+1'.
	 * The returned fitness is the number of correctly predicted pixels.
	 */
	int fitness (const EightLayerIM& elim, int firstLayer, int lastLayer) const;
	int fitness (const EightLayerIM* elim, int firstLayer, int lastLayer) const;
	void mutate (void);
	void initiateRandom(int ruleNum);
	void xOver (const SEARuleSet& parent1, const SEARuleSet& parent2);
	void trimToSize (int ruleNum);

	/**
	 * Applies the current SEARuleset to the given 8layerIM, using only the history from 
	 * firstLayer to lastLayer. It then creates the layer lastLayer+1 in the elim.
	 */
	void advance (EightLayerIM& elim, int firstLayer, int lastLayer);

	/**
	 * Applies the current SEARuleset to the given 8layerIM, using only the history from 
	 * firstLayer to lastLayer. It then saves the result in the targer ImageMatrix.
	 */
	void applyOnELIM (const EightLayerIM& elim, int firstLayer, int lastLayer, ImageMatrix& target) const;

	/** Returns the number of rules in the current ruleset. */
	int getRuleNum() const;

	/** Returns the total number of partial conditions in all the rules of the ruleset. */
	int getTotalPartialConditionNum () const;

	int getNetInfoSizeInBits () const;

	friend ostream& operator<< (ostream& stream, const SEARuleSet& ruleset);
};